﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace test
{
    class Program
    {
        public Bitmap ToGrayNormal(Bitmap source_image, int[] grayHistogram)
        {
            int maxGray = -1, minGray = 256;
            for (int i = 0; i < 256; i++)
            {
                grayHistogram[i] = 0;
            }
            BitmapData srcDataStream = source_image.LockBits(new Rectangle(0, 0, source_image.Width, source_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            Bitmap output_image = new Bitmap(source_image.Width, source_image.Height, PixelFormat.Format24bppRgb);
            BitmapData outDataStream = output_image.LockBits(new Rectangle(0, 0, output_image.Width, output_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            unsafe
            {
                byte* srcHead = (byte*)srcDataStream.Scan0;
                byte* outHead = (byte*)outDataStream.Scan0;
                int i = 0;
                for (int y = 0; y < source_image.Height; y++)
                {
                    for (int x = 0; x < source_image.Width; x++)
                    {
                        //int gray = (9798 * srcHead[i] + 19235 * srcHead[i + 1] + 3735 * srcHead[i + 2]) / 32768;
                        int gray = (srcHead[i] + srcHead[i + 1] + srcHead[i + 2]) / 3;
                        maxGray = maxGray > gray ? maxGray : gray;
                        minGray = minGray < gray ? minGray : gray;
                        outHead[i] = (byte)gray;
                        outHead[i + 1] = (byte)gray;
                        outHead[i + 2] = (byte)gray;
                        i += 3;
                        grayHistogram[gray]++;
                    }
                }
            }
            source_image.UnlockBits(srcDataStream);
            output_image.UnlockBits(outDataStream);
            string line = "maxGray:" + maxGray.ToString();
            Console.WriteLine(line);
            line = "minGray:" + minGray.ToString();
            Console.WriteLine(line);
            return output_image;
        }

        public Bitmap ToBlackWhite(Bitmap source_image, int threshold)
        {
            BitmapData srcDataStream = source_image.LockBits(new Rectangle(0, 0, source_image.Width, source_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            Bitmap output_image = new Bitmap(source_image.Width, source_image.Height, PixelFormat.Format24bppRgb);
            BitmapData outDataStream = output_image.LockBits(new Rectangle(0, 0, output_image.Width, output_image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            unsafe
            {
                byte* srcHead = (byte*)srcDataStream.Scan0;
                byte* outHead = (byte*)outDataStream.Scan0;
                int i = 0;
                for (int y = 0; y < source_image.Height; y++)
                {
                    for (int x = 0; x < source_image.Width; x++)
                    {
                        int gray = srcHead[i] > threshold ? 255 : 0;
                        outHead[i] = (byte)gray;
                        outHead[i + 1] = (byte)gray;
                        outHead[i + 2] = (byte)gray;
                        i += 3;
                    }
                }
            }
            source_image.UnlockBits(srcDataStream);
            output_image.UnlockBits(outDataStream);
            return output_image;
        }

        private int GetAverageGray(int[] grayHistogram, int start, int end)
        {
            if (start > end) return end;
            int total = 0, sum = 0;
            for (int i = start; i <= end; i++)
            {
                total += grayHistogram[i];
                sum += i * grayHistogram[i];
            }
            return sum / total;
        }

        public int GetThresholdBasic(int[] grayHistogram, int delta = 0)
        {
            int lastThreshold = GetAverageGray(grayHistogram, 0, 255);
            int nowThreshold;
            while (true)
            {
                int m1 = GetAverageGray(grayHistogram, 0, lastThreshold);
                int m2 = GetAverageGray(grayHistogram, lastThreshold + 1, 255);
                nowThreshold = (m1 + m2) / 2;
                if (nowThreshold - lastThreshold <= delta) break;
                lastThreshold = nowThreshold;
            }
            return nowThreshold;
        }

        public int GetThresholdOtsu(int[] grayHistogram)
        {
            double[] p = new double[256];
            double[] P = new double[256];
            double[] m = new double[256];
            double[] sgmB2 = new double[256];
            double total = 0;
            for (int i = 0; i < 256; i++)
            {
                total += grayHistogram[i];
            }
            double sum  = 0, mG = 0, maxsgm = -1;
            int k = -1;
            for (int i = 0; i < 256; i++)
            {
                p[i] = grayHistogram[i] / total;
                sum += p[i];
                P[i] = sum;
                mG += i * p[i];
                m[i] = mG;
            }
            for (int i = 0; i < 256; i++)
            {
                sgmB2[i] = (mG * P[i] - m[i]) * (mG * P[i] - m[i]) / P[i] / (1 - P[i]);
                if (sgmB2[i] > maxsgm)
                {
                    maxsgm = sgmB2[i];
                    k = i;
                }
            }
            return k;
        }

        public int GetThresholdEntropy(int[] grayHistogram)
        {
            double[] p = new double[256];
            double[] P = new double[256];
            double total = 0;
            for (int i = 0; i < 256; i++)
            {
                total += grayHistogram[i];
            }
            double sum = 0, max = -1;
            int k = -1;
            for (int i = 0; i < 256; i++)
            {
                p[i] = grayHistogram[i] / total;
                sum += p[i];
                P[i] = sum;
            }
            for (int t = 0; t < 256; t++)
            {
                double hb = 0, hw = 0;
                for (int i = 0; i < 256; i++)
                {
                    double temp;
                    if (i <= t)
                    {
                        temp = p[i] / P[t];
                        hb += temp * Math.Exp(1 - temp);
                    } 
                    else
                    {
                        temp = p[i] / (1 - P[t]);
                        hw += temp * Math.Exp(1 - temp);
                    }
                }
                double now = hb + hw;
                if (now > max)
                {
                    max = now;
                    k = t;
                }
            }
            return k;
        }

        static void Main(string[] args)
        {
            Program p = new Program();

            //string folder = @"C:\Users\titanxxh\Desktop\";
            string folder;
            try
            {
                folder = args[0];
            }
            catch (System.Exception ex)
            {
                folder = "";
            }
            string file = folder + "source.jpg";
            int[] grayHistogram = new int[256];
            Bitmap source_image = new Bitmap(file);
            Bitmap gray_image = p.ToGrayNormal(source_image, grayHistogram);
            string grayoutputpath = folder+"0gray.jpg";
            gray_image.Save(grayoutputpath, ImageFormat.Jpeg);
            int count = 3;
            int[] threshold = new int[count+1];
            threshold[1] = p.GetThresholdBasic(grayHistogram);
            threshold[2] = p.GetThresholdOtsu(grayHistogram);
            threshold[3] = p.GetThresholdEntropy(grayHistogram);
            string[] method = { "", "1basic", "2otsu", "3entropy" };
            for (int i = 1; i <= count; i++ )
            {
                string line = string.Format("{0} method threshold: {1}", method[i], threshold[i]);
                Console.WriteLine(line);
                string bwoutputpath = string.Format("{1}{0}.jpg", method[i], folder);
                Bitmap blackwhite_image = p.ToBlackWhite(gray_image, threshold[i]);
                blackwhite_image.Save(bwoutputpath, ImageFormat.Jpeg);
            }
        }
    }
}
